package com.fishery.customization.dao.reposity;

import com.fishery.customization.entity.Page;
import com.fishery.customization.util.DataUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public abstract class BaseDao<T> implements MongoDao<T> {

	@Autowired
	protected MongoOperations mongoOperations;

	private Class<T> c;

	private String collectionName;

	private String idName;

	{
		// 获取泛型T的Class对象
		ParameterizedType genericSuperclass = (ParameterizedType) (this.getClass().getGenericSuperclass());
		c = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
		// 初始化集合名
		collectionName = c.getAnnotation(Document.class).value();
		// 初始化id字段名
		idName = initIdName(c);
	}

	/**
	 * 初始化id字段名
	 *
	 * @param c
	 * @return
	 */
	private String initIdName(Class c) {
		for (Field field : c.getDeclaredFields()) {
			if (field.isAnnotationPresent(Id.class)) {
				return field.getName();
			}
		}
		return "id";
	}

	/**
	 * 获取id字段名
	 *
	 * @return
	 */
	protected String getIdName() {
		return idName;
	}

	/**
	 * 获取结合名
	 *
	 * @return
	 */
	protected String getCollectionName() {
		return collectionName;
	}

	/**
	 * 获取id字段的值
	 *
	 * @param entity
	 * @return
	 */
	protected Object getIdValue(T entity) {
		try {
			Field field = entity.getClass().getDeclaredField(idName);
			field.setAccessible(true);
			return field.get(entity);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public long count() {
		return mongoOperations.count(new Query(), collectionName);
	}

	@Override
	public List<T> findAll() {
		return mongoOperations.findAll(c, collectionName);
	}

	@Override
	public Page<T> findAllWithPage(int current, int size) {
		Query query = new Query();
		query.with(PageRequest.of(current - 1, size));
		List<T> records = mongoOperations.find(query, c, collectionName);
		long total = count();
		return new Page<T>(current, size, records, total);
	}

	@Override
	public long deleteById(String id) {
		return mongoOperations.remove(Query.query(getIdCriteria(id)), c, collectionName)
				.getDeletedCount();
	}

	@Override
	public long deleteAll(List<String> ids) {
		try {
			ids.forEach(id -> {
				deleteById(id);
			});
			return 1;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}


	private Criteria getIdCriteria(Object id) {
		return Criteria.where(idName).is(id);
	}

	@Override
	public long updateById(T entity) {
		Update update = new Update();
		DataUtil.fill(update, entity);
		return mongoOperations.updateFirst(Query.query(getIdCriteria(getIdValue(entity))),
				update, c, collectionName).getModifiedCount();
	}

	@Override
	public long insert(T entity) {
		try {
			mongoOperations.insert(entity, collectionName);
			return 1;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	@Override
	public long insertAll(List<T> list) {
		try {
			list.forEach(draggableModule -> {
				insert(draggableModule);
			});
			return 1;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}

	@Override
	public T findById(String id) {
		return mongoOperations.findOne(Query.query(getIdCriteria(id)), c, collectionName);
	}

	public <S extends T> Optional<S> findOne(Example<S> example) {
		if (example == null) {
			throw new NullPointerException("Example is null");
		}
		return Optional.ofNullable(mongoOperations.
				findOne(Query.query(Criteria.byExample(example)), example.getProbeType(), collectionName));
	}


	protected Query getBasicQuery(Map<String, Object> queryMap) {
		return new BasicQuery(new org.bson.Document(queryMap));
	}

	protected Query getBasicQuery(Map<String, Object> queryMap, Map<String, Object> fieldsMap) {
		return new BasicQuery(new org.bson.Document(queryMap), new org.bson.Document(fieldsMap));
	}

	protected Query getBasicQuery(String queryKey, Object queryValue, String fieldKey, Object fieldValue) {
		return new BasicQuery(new org.bson.Document(queryKey, queryValue),
				new org.bson.Document(fieldKey, fieldValue));
	}

	protected Query getBasicQuery(String queryKey, Object queryValue) {
		return new BasicQuery(new org.bson.Document(queryKey, queryValue));
	}

	protected Query getBasicQuery(String queryKey, Object queryValue, Map<String, Object> fieldsMap) {
		return new BasicQuery(new org.bson.Document(queryKey, queryValue),
				new org.bson.Document(fieldsMap));
	}

}
